/**
 * Copyright 2008 - 2009 Pro-Netics S.P.A.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package it.pronetics.madstore.hatom.alfresco.web.bean;

import it.pronetics.madstore.hatom.alfresco.util.HAtomDialogUtils;
import it.pronetics.madstore.hatom.alfresco.util.SelectItemUtils;
import it.pronetics.madstore.hatom.alfresco.util.XomUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;

import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;

import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.bean.dialog.BaseDialogBean;

/**
 * Bean implementation for the "Add hAtom Microformats" dialog
 * 
 * @author Salvatore Incandela
 */

public class AddhAtomXslDialog extends BaseDialogBean {

	private ContentService contentService;

	private NodeRef nodeRef;
	private Document document;
	private List<SelectItem> xslSelects = new ArrayList<SelectItem>();
	private List<SelectItem> xslNotToSelect = new ArrayList<SelectItem>();
	private Map<String, Element> id2Elements = new TreeMap<String, Element>();

	private String entryTitle;
	private String entrySummary;
	private String entryContent;
	private String entryAuthor;
	private String entryUpdated;

	private List<HEntryBean> selectedHEntries = new ArrayList<HEntryBean>();
	protected transient DataModel selectedEntryDataModel;

	private List<HEntryBean> addedHEntries = new ArrayList<HEntryBean>();
	private List<HEntryBean> addedHEntriesToDel = new ArrayList<HEntryBean>();
	protected transient DataModel addedEntryDataModel;

	private boolean isRemovedAnAddedHEntry = false;
	private boolean isUpdatedAnAddedHEntry = false;
	private HEntryBean entryToUpdate = null;

	@Override
	public void init(Map<String, String> parameters) {
		super.init(parameters);
		this.clear();
		this.nodeRef = this.browseBean.getActionSpace().getNodeRef();
		ContentReader reader = this.contentService.getReader(nodeRef,
				ContentModel.PROP_CONTENT);
		String content = reader.getContentString();
		try {
			this.document = (new Builder()).build(content, "");
		} catch (Exception e) {
			e.printStackTrace();
		}
		this.xslSelects.add(new SelectItem(SelectItemUtils.NOT_SELECTED));
		Node root = this.document.getRootElement();
		retrieveXslSelectsFromBody(root);
	}

	@Override
	protected String finishImpl(FacesContext context, String outcome)
			throws Exception {
		deleteAddedHEntries(this.addedHEntriesToDel);
		addMicroformatsHAtom(this.selectedHEntries);
		ContentWriter writer = this.contentService.getWriter(nodeRef,
				ContentModel.PROP_CONTENT, true);
		writer.putContent(this.document.toXML());
		this.clear();
		return "dialog:close";
	}

	@Override
	public boolean getFinishButtonDisabled() {
		if (this.selectedHEntries.isEmpty() && !this.isRemovedAnAddedHEntry
				&& !this.isUpdatedAnAddedHEntry)
			return true;
		return false;
	}

	/**
	 * This method is used to reset the state of this Java Bean.
	 */

	private void clear() {
		this.xslSelects.clear();
		this.xslNotToSelect.clear();
		this.id2Elements.clear();
		this.selectedHEntries.clear();
		this.addedHEntries.clear();
		this.addedHEntriesToDel.clear();
		this.entryAuthor = null;
		this.entryTitle = null;
		this.entrySummary = null;
		this.entryUpdated = null;
		this.entryContent = null;
		this.isRemovedAnAddedHEntry = false;
	}

	/**
	 * This method is used to delete added hEntries.
	 * 
	 * @param addedHEntriesToDel
	 */

	private void deleteAddedHEntries(List<HEntryBean> addedHEntriesToDel) {
		for (HEntryBean entryToDelete : addedHEntriesToDel) {
			HAtomDialogUtils.deleteHEntry(entryToDelete, this.id2Elements);
		}
	}

	/**
	 * This method adds the hAtom Microformats for the hEntries defined by the
	 * user.
	 * 
	 * @param selectedHEntries
	 *            List of the hEntries defined by the user.
	 */

	private void addMicroformatsHAtom(List<HEntryBean> selectedHEntries) {
		for (HEntryBean hEntry : selectedHEntries) {
			List<Node> newElements = new ArrayList<Node>();
			newElements.add(HAtomDialogUtils.addMicroformatsHAtomToOneNode(
					XomUtils.SPAN, hEntry.getTitle(),
					HAtomDialogUtils.HATOM_TITLE, id2Elements));
			newElements.add(HAtomDialogUtils.addMicroformatsHAtomToOneNode(
					XomUtils.SPAN, hEntry.getSummary(),
					HAtomDialogUtils.HATOM_SUMMARY, id2Elements));
			newElements.add(HAtomDialogUtils.addMicroformatsHAtomToOneNode(
					XomUtils.SPAN, hEntry.getContent(),
					HAtomDialogUtils.HATOM_CONTENT, id2Elements));
			newElements.add(HAtomDialogUtils.addMicroformatsHAtomToOneNode(
					XomUtils.SPAN, hEntry.getUpdated(),
					HAtomDialogUtils.HATOM_UPDATED, id2Elements));
			newElements.add(HAtomDialogUtils.addMicroformatsHAtomToOneNode(
					XomUtils.ADDRESS, hEntry.getAuthor(),
					HAtomDialogUtils.HATOM_AUTHOR, id2Elements));
			HAtomDialogUtils.addMicroformatsHEntry(XomUtils
					.removeNullsNodes(newElements));
		}
	}

	/**
	 * This method retrieves all the Element Node of type <xsl:value-of
	 * select="..." /> contained in the Element Node <body>
	 * 
	 * @param node
	 *            Root Node
	 */

	private void retrieveXslSelectsFromBody(Node node) {
		boolean isBody = false;
		if (node instanceof Element) {
			Element element = (Element) node;
			if (element.getQualifiedName().equals("body")) {
				isBody = true;
			}
		}
		if (isBody) {
			retrieveXslSelects(node);
		} else {
			for (int i = 0; i < node.getChildCount(); i++) {
				retrieveXslSelectsFromBody(node.getChild(i));
			}
		}
	}

	/**
	 * This method retrieves the Element nodes for the JSF Select form item. The
	 * search is done starting from a defined Node.
	 * 
	 * @param node
	 *            Defined Node
	 */

	private void retrieveXslSelects(Node node) {
		if (node instanceof Element) {
			Element element = (Element) node;
			String nameTag = element.getQualifiedName();
			if (nameTag.equals("xsl:value-of")) {
				SelectItem item = SelectItemUtils.getSelectItem(element);
				this.id2Elements.put((String) item.getValue(), element);
				this.xslSelects.add(item);
			}
		}
		for (int i = 0; i < node.getChildCount(); i++) {
			Node child = node.getChild(i);
			if (!isHEntry(child)) {
				retrieveXslSelects(child);
			} else {
				HEntryBean entry = new HEntryBean();
				HAtomDialogUtils.retrieveAndStoreHentry(child, entry,
						this.id2Elements);
				entry = new HEntryBean(entry);
				this.addedHEntries.add(entry);
				this.id2Elements.put(entry.getName(), (Element) child);
			}
		}
	}

	/**
	 * This method checks if the passed Node is an hEntry.
	 * 
	 * @param node
	 *            Node to check.
	 * @return True if the passed Node is an hEntry, false otherwise.
	 */

	private boolean isHEntry(Node node) {
		if (node instanceof Element) {
			Element elementChild = (Element) node;
			Attribute classAttribute = elementChild.getAttribute("class");
			if (classAttribute != null) {
				String classDef = classAttribute.getValue();
				if (classDef.equals(HAtomDialogUtils.HATOM_HENTRY)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Action used to add a defined hEntry waiting to be processed.
	 * 
	 * @param event
	 */

	public void addSelectedhEntryBean(final ActionEvent event) {
		this.isRemovedAnAddedHEntry = false;
		if (!isConflict() && isRequiredFieldsDefined()) {
			HEntryBean selectedhEntry = new HEntryBean(this.entryTitle,
					this.entrySummary, this.entryContent, this.entryUpdated,
					this.entryAuthor);
			this.selectedHEntries.add(selectedhEntry);
			List<SelectItem> itemsToRemove = new ArrayList<SelectItem>();
			for (SelectItem item : this.xslSelects) {
				if (!((String) item.getValue())
						.equals(SelectItemUtils.NOT_SELECTED)) {
					if (((String) item.getValue()).equals(this.entrySummary)
							|| ((String) item.getValue())
									.equals(this.entryContent)
							|| ((String) item.getValue())
									.equals(this.entryUpdated)
							|| ((String) item.getValue())
									.equals(this.entryAuthor)
							|| ((String) item.getValue())
									.equals(this.entryTitle)) {
						itemsToRemove.add(item);
						this.xslNotToSelect.add(item);
					}
				}
			}
			for (SelectItem itemToRemove : itemsToRemove) {
				this.xslSelects.remove(itemToRemove);
			}
		}
	}

	/**
	 * Action used to remove a defined hEntry waiting to be processed.
	 * 
	 * @param event
	 */

	public synchronized void removeSelectedHEntryBean(final ActionEvent event) {
		final HEntryBean wrapper = (HEntryBean) this.selectedEntryDataModel
				.getRowData();
		if (wrapper != null) {
			this.selectedHEntries.remove(wrapper);
			List<SelectItem> itemsToReAdd = new ArrayList<SelectItem>();
			for (SelectItem item : this.xslNotToSelect) {
				if (!((String) item.getValue())
						.equals(SelectItemUtils.NOT_SELECTED)) {
					if (((String) item.getValue()).equals(wrapper.getAuthor())
							|| ((String) item.getValue()).equals(wrapper
									.getContent())
							|| ((String) item.getValue()).equals(wrapper
									.getSummary())
							|| ((String) item.getValue()).equals(wrapper
									.getTitle())
							|| ((String) item.getValue()).equals(wrapper
									.getUpdated())) {
						itemsToReAdd.add(item);
						this.xslSelects.add(item);
					}
				}
			}
			for (SelectItem itemToRemove : itemsToReAdd) {
				this.xslNotToSelect.remove(itemToRemove);
			}
			this.entryTitle = null;
			this.entrySummary = null;
			this.entryContent = null;
			this.entryAuthor = null;
			this.entryUpdated = null;
		}
	}

	/**
	 * This Action implements the Delete Operation of CRUD.
	 * 
	 * @param event
	 */

	public synchronized void deleteAddedHEntryBean(final ActionEvent event) {
		final HEntryBean entryToDelete = (HEntryBean) this.addedEntryDataModel
				.getRowData();
		this.addedHEntries.remove(entryToDelete);
		this.addedHEntriesToDel.add(entryToDelete);
		this.isRemovedAnAddedHEntry = true;
		this.entryAuthor = null;
		this.entryContent = null;
		this.entrySummary = null;
		this.entryTitle = null;
		this.entryUpdated = null;
	}

	/**
	 * This Action implements the Update Operation of CRUD
	 * 
	 * @param event
	 */

	public synchronized void updateAddedHEntryBean(final ActionEvent event) {
		this.entryToUpdate = (HEntryBean) this.addedEntryDataModel.getRowData();
		this.isUpdatedAnAddedHEntry = true;
		this.entryAuthor = null;
		this.entryContent = null;
		this.entrySummary = null;
		this.entryTitle = null;
		this.entryUpdated = null;
	}

	/**
	 * This method checks if there are different hAtom properties with same
	 * value.
	 * 
	 * @return true if there are different hAtom properties with same value.
	 */

	public boolean isConflict() {
		List<String> listString = new ArrayList<String>();
		listString.add(entryAuthor);
		listString.add(entryContent);
		listString.add(entrySummary);
		listString.add(entryTitle);
		listString.add(entryUpdated);
		for (String property : listString) {
			int occur = HAtomDialogUtils.occurInList(property, listString);
			if (occur > 1) {
				return true;
			}
		}
		return false;
	}

	/**
	 * This method checks if the required fields are selected.
	 * 
	 * @return
	 */

	public boolean isRequiredFieldsDefined() {
		if (this.entryTitle != null && this.entryUpdated != null) {
			return !this.entryTitle.equals(SelectItemUtils.NOT_SELECTED)
					&& !this.entryUpdated.equals(SelectItemUtils.NOT_SELECTED);
		} else if (this.entryTitle == null && this.entryUpdated != null) {
			return !this.entryUpdated.equals(SelectItemUtils.NOT_SELECTED);
		} else if (this.entryTitle != null && this.entryUpdated == null) {
			return !this.entryTitle.equals(SelectItemUtils.NOT_SELECTED);
		} else
			return true;
	}

	// GETTERS AND SETTERS //

	public ContentService getContentService() {
		return contentService;
	}

	public void setContentService(ContentService contentService) {
		this.contentService = contentService;
	}

	public NodeRef getNodeRef() {
		return nodeRef;
	}

	public void setNodeRef(NodeRef nodeRef) {
		this.nodeRef = nodeRef;
	}

	public List<SelectItem> getXslSelects() {
		return xslSelects;
	}

	public void setXslSelects(List<SelectItem> xslSelects) {
		this.xslSelects = xslSelects;
	}

	public Document getDocument() {
		return document;
	}

	public void setDocument(Document document) {
		this.document = document;
	}

	public String getEntryTitle() {
		return this.entryTitle;
	}

	public void setEntryTitle(String entryTitle) {
		this.entryTitle = entryTitle;
	}

	public String getEntrySummary() {
		return this.entrySummary;
	}

	public void setEntrySummary(String entrySummary) {
		this.entrySummary = entrySummary;
	}

	public String getEntryContent() {
		return this.entryContent;
	}

	public void setEntryContent(String entryContent) {
		this.entryContent = entryContent;
	}

	public String getEntryAuthor() {
		return this.entryAuthor;
	}

	public void setEntryAuthor(String entryAuthor) {
		this.entryAuthor = entryAuthor;
	}

	public String getEntryUpdated() {
		return entryUpdated;
	}

	public void setEntryUpdated(String entryUpdated) {
		this.entryUpdated = entryUpdated;
	}

	public DataModel getSelectedEntryDataModel() {
		if (this.selectedEntryDataModel == null) {
			this.selectedEntryDataModel = new ListDataModel();
		}
		this.selectedEntryDataModel.setWrappedData(this.selectedHEntries);
		return this.selectedEntryDataModel;
	}

	public void setSelectedEntryDataModel(DataModel entryDataModel) {
		this.selectedEntryDataModel = entryDataModel;
	}

	public DataModel getAddedEntryDataModel() {
		if (this.addedEntryDataModel == null) {
			this.addedEntryDataModel = new ListDataModel();
		}
		this.addedEntryDataModel.setWrappedData(this.addedHEntries);
		return this.addedEntryDataModel;
	}

	public void setAddedEntryDataModel(DataModel addedEntryDataModel) {
		this.addedEntryDataModel = addedEntryDataModel;
	}

	public Map<String, Element> getId2Elements() {
		return id2Elements;
	}

	public void setId2Elements(Map<String, Element> id2Elements) {
		this.id2Elements = id2Elements;
	}

	public HEntryBean getEntryToUpdate() {
		return entryToUpdate;
	}

	public void setEntryToUpdate(HEntryBean hentryBeanToUpdate) {
		this.entryToUpdate = hentryBeanToUpdate;
	}

	public List<HEntryBean> getAddedHEntries() {
		return addedHEntries;
	}

	public void setAddedHEntries(List<HEntryBean> addedHEntries) {
		this.addedHEntries = addedHEntries;
	}

	public boolean isUpdatedAnAddedHEntry() {
		return isUpdatedAnAddedHEntry;
	}

	public void setUpdatedAnAddedHEntry(boolean isUpdatedAnAddedHEntry) {
		this.isUpdatedAnAddedHEntry = isUpdatedAnAddedHEntry;
	}
}